</center> Автор материала: программист-исследователь Mail.ru Group, старший преподаватель Факультета Компьютерных Наук ВШЭ Юрий Кашницкий. Материал распространяется на условиях лицензии Creative Commons CC BY-NC-SA 4.0. Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала.
Идеальный просмотр Jupyter notebooks возможен только локально, GitHub и nbviewer могут неправильно отбражать формулы или картинки.
В задании предлагается с помощью Pandas ответить на несколько вопросов по данным о сердечно-сосудистых заболеваниях (качать данные не надо, они уже есть в репозитории). Данные использовались в соревновании Ml Boot Camp 5.
Заполните код в клетках (где написано "Ваш код здесь") и ответьте на вопросы в веб-форме.
В соревновании предлагалось определить наличие/отсутствие сердечно-сосудистых заболеваний (ССЗ) по результатам осмотра пациента.
Описание данных.
Объективные признаки:
Результаты измерения:
Субъективные признаки (со слов пациентов):
Целевой признак (который интересно будет прогнозировать):
Значения показателей холестерина и глюкозы представлены одним из трех классов: норма, выше нормы, значительно выше нормы. Значения субъективных признаков — бинарны.
Все показатели даны на момент осмотра.
Мы будем работать только с обучающей выборкой и с помощью Pandas
проведем первичный анализ данных.
Из библиотек нам понадобятся только NumPy
и Pandas
.
In [67]:
import numpy as np
import pandas as pd
Считываем данные из CSV-файла в объект pandas DataFrame.
In [66]:
df = pd.read_csv('../../data/mlbootcamp5_train.csv', sep=';',
index_col='id')
Посмотрим на первые 5 записей.
In [68]:
df.head()
Out[68]:
Вопрос 1 (1 балл). Сколько мужчин и женщин представлено в этом наборе данных? Не было дано расшифровки признака "пол" (какому полу соответствует 1, а какому – 2 в признаке gender
) – это определите, посмотрев также на рост при разумном предположении, что в среднем мужчины выше.
Варианты:
In [47]:
print("{0} {1} и {2} {3}".format(
df[df['gender'] == 1]['height'].count(),
"женщин" if df[df['gender'] == 1]['height'].mean() < df[df['gender'] == 2]['height'].mean() else "мужчин",
df[df['gender'] == 2]['height'].count(),
"женщин" if df[df['gender'] == 1]['height'].mean() > df[df['gender'] == 2]['height'].mean() else "мужчин"
))
Вопрос 2 (1 балл). Кто в среднем чаще указывает, что употребляет алкоголь – мужчины или женщины?
Варианты:
In [48]:
print("женщины" if df[df['gender'] == 1]['alco'].mean() > df[df['gender'] == 2]['alco'].mean() else "мужчины")
Вопрос 3 (1 балл). Во сколько раз (округленно, round
) процент курящих среди мужчин больше, чем процент курящих среди женщин (по крайней мере, по этим анкетным данным)?
Варианты:
In [49]:
round((df[df['gender'] == 2]['smoke'].mean() * 100) / (df[df['gender'] == 1]['smoke'].mean() * 100))
Out[49]:
Вопрос 4 (1 балл). Вы наверняка заметили, что значения возраста какие-то странные. Догадайтесь, в чем здесь измеряется возраст, и ответьте, на сколько месяцев (примерно) отличаются медианные значения возраста курящих и некурящих.
Варианты:
In [50]:
round((df[df['smoke'] == 0]['age'].median() - df[df['smoke'] == 1]['age'].median()) / 365.25 * 12)
Out[50]:
Вопрос 5 (2 балла). В статье на Википедии про сердечно-сосудистый риск показана шкала SCORE для расчёта риска смерти от сердечно-сосудистого заболевания в ближайшие 10 лет. Вот она:
Давайте посмотрим на правый верхний прямоугольник, отображающий сегмент курящих мужчин в возрасте от 60 до 64 лет включительно. (Неочевидно, но тут для возраста и давления цифры означают верхнюю границу, и она не включается).
Видим 9-ку в левом нижнем углу этого прямоугольника и 47 – в правом верхнем. То есть если при этом систолическое (т.е. верхнее) артериальное давление – меньше 120 мм рт.ст., а уровень холестерина – 4 ммоль/л, то риск ССЗ оценивается примерно в 5 раз ниже, чем если бы давление лежало в интервале [160, 180), а холестерина было бы 8 ммоль/л.
Давайте посчитаем аналогичное значение, но на наших данных.
Уточнения:
age_years
– возраст в годах, округлив до целых (round
). Для данного примера отберите курящих мужчин от 60 до 64 лет включительноcholesterol
следующее: 4 ммоль/л $\rightarrow$ 1, 5-7 ммоль/л $\rightarrow$ 2, 8 ммоль/л $\rightarrow$ 3.Во сколько раз (округленно, round
) отличаются доли больных людей в этих двух подвыборках? Посчитайте на наших данных.
Варианты:
In [82]:
df['age_years'] = round(df['age'] / 365.25)
subdf = df[(df['age_years'] >= 60) & (df['age_years'] <= 64)]
subdf[(subdf['ap_hi'] < 120) & (subdf['cholesterol'] == 1)].shape, subdf[(subdf['ap_hi'] > 160) & (subdf['ap_hi'] < 180) & (subdf['cholesterol'] == 3)].shape
Out[82]:
Вопрос 6 (2 балла). Постройте новый признак – BMI (Body Mass Index). Для этого надо вес в килограммах поделить на квадрат роста в метрах. Нормальными считаются значения BMI от 18.5 до 25. Выберите верные утверждения.
Утверждения:
In [76]:
df['BMI'] = df['weight'] / (df['height'] / 100) ** 2
print(
df['BMI'].median() >= 18.5 and df['BMI'].median() <= 25, "- "
"Медианный BMI по выборке лежит в пределах нормы",
)
print(
df[df['gender'] == 1]['BMI'].mean() > df[df['gender'] == 2]['BMI'].mean(), "- "
"У женщин в среднем BMI выше, чем у мужчин",
)
print(
df[df['cardio'] == 0]['BMI'].mean() > df[df['cardio'] == 1]['BMI'].mean(), "- "
"У здоровых в среднем BMI выше, чем у больных"
)
subdf = df[(df['alco'] == 0) & (df['cardio'] == 0)]
print(
(subdf[subdf['gender'] == 2]['BMI'].mean()) < (subdf[subdf['gender'] == 1]['BMI'].mean()), "- "
"В сегменте здоровых и непьющих мужчин BMI ближе к норме, чем в сегменте здоровых и непьющих женщин"
)
Вопрос 7 (2 балла). Можно заметить, что данные не особо-то чистые, много в них всякой "грязи" и неточностей. Еще лучше мы это увидим, когда обсудим визуализацию данных.
Отфильтруйте следующие сегменты пациентов (считаем это ошибками в данных)
pd.Series.quantile
, если не знаете, что это такое – прочитайте)Этот вовсе не вся чистка данных, которую можно было проделать, но пока остановимся на этом.
Сколько процентов данных (округленно, round
) мы выбросили?
Варианты:
In [78]:
bad_df = df[
(df['ap_lo'] > df['ap_hi']) |
(df['height'] < df['height'].quantile(.025)) |
(df['height'] > df['height'].quantile(.975)) |
(df['weight'] < df['weight'].quantile(.025)) |
(df['weight'] > df['weight'].quantile(.975))
]
round( bad_df['age'].count() * 100 / df['age'].count() )
Out[78]: